<?php
/* 
 * API传输加密类
 * @date 2015-12-08
 * @modifydate 2015-12-09
 * @author wzl
 * 使用方法：请求用send 函数, 接受用seceive 函数
 */
class ApiBase {
    /*定义新数组*/
    protected $values = array();
    protected static $KEY = '';//密钥，双方协定，请勿泄露
    
    /**
     * 初使化
     * @param string $key 【可选】设置密钥，为空则读取默认的
     */
    public function __construct($key = '') {        
        if($key != '') {
            self::$KEY = $key;
        }
        date_default_timezone_set('PRC');//初使北京时间
    }
    public static function load(){
        return new self();
    }

    /**
     * 获取curl 请求过来的数据。
     * @return string 
     */
    public function getRequest(){
        return file_get_contents("php://input");
    }
    /**
     * 获取整个数组
     * @return array 返回数组
     */
    public function getValues(){
        return $this->values;
    }
    /**
     * 返回请求的data数据
     * @return mix 返回请求的数据
     */
    public function getData() {
        return $this->values['data'];
    }
    /**
     * 获取数组中的SIGN
     * @return string 返回大写的SIGN
     */
    public function getSign() {
        return $this->values['sign'];
    }
    /**
     * 设置需要传输的数据
     * @param array $data 数组，不限维度
     */
    public function setData($data) {
        $this->values['data'] = $data;
    }
    
    /**
     * 设置SIGN
     * @return string 返回大写的SIGN
     */
    public function setSign(){
        $sign = $this->makeSign();
        $this->values['sign'] = $sign;        
        return $sign;
    }
    /**
     * 将JSON转换成ARRAY
     * @param string $json 数据
     * @return array 数组
     */
    public function ToArray($json){
        $this->values = json_decode($json, true);
        return $this->values;
    }
    /**
     * 将ARRAY转换换JSON
     * @return string json数据
     */
    public function toJson() {
        ksort($this->values);
        return json_encode($this->values);
    }  
    /**
     * 生成SIGN
     * @return string 返回大写SIGN
     */
    public function makeSign() {    
        $param_arr = $this->toOneArray();
        unset($param_arr['sign']);//删除sign，不参与加密 
        ksort($param_arr);//词典排序
        $param_string = implode("&", $param_arr);//将数组转换成字符串
        $sign = md5(md5($param_string) . self::$KEY);//加密形成SIGN
        return strtoupper($sign);//转换成大写
    }
    /**
     * 设置唯一变化值。以防攻击 
     */
    public function setOnly(){
        $this->setTime();
        $this->setNonce();
    }
    /**
     * 设置时间
     */
    protected function setTime(){
        $this->values['time'] = time();
    }
    /**
     * 设置随机码 
     */
    protected function setNonce(){
        $this->values['nonce'] = $this->getNonceStr();
    }

    /**
    * 
    * 产生随机字符串，不长于32位
    * @param int $length
    * @return 产生的随机字符串
    */
    public static function getNonceStr($length = 32) 
    {
        $chars = "abcdefghijklmnopqrstuvwxyz0123456789";  
        $str ="";
        for ( $i = 0; $i < $length; $i++ )  {  
                $str .= substr($chars, mt_rand(0, strlen($chars)-1), 1);  
        } 
        return $str;
    }
    /**
     * 获取唯一码
     * @param type $length
     * @param type $start
     * @return type
     */
    public function get_unique_id($length = 32, $start = 'X') {
        $nonce = $this->getNonceStr($length);
        $id = md5(uniqid(mt_rand(0, 999999999), true) . md5($nonce));
        $_length = strlen($id) > $length ? $length : strlen($id);
        return strtoupper($start) . strtoupper(substr($id, 0, $_length - strlen($start)));
    }
    /**
     * 设置返回值，
     * @param int $status 返回状态值 0为成功，其它值异常
     * @param mix $data 【可选】返回的数据
     * @return string 返回的json值 （已将array转换成json）
     */
    public function setReturn($status, $data = array()) {
        $return = array(
            'status'=> $status,
            'data'  => $data, 
        );
        return json_encode($return);
    }
    /**
     * 将多维数组转换成一维数组
     */
    protected function toOneArray() {
        $param_arr = array();//新定义一个数组
        if(count($this->values) > 0) {            
            foreach ($this->values as $key => $val) {
                if(is_array($val)) {//数组的则进行序列化
                    $param_arr[$key] = serialize($val);
                } else {//非数组的不序列化
                    $param_arr[$key] = $val;
                }
            }
        }
        return $param_arr;
    }
    /**
     * curl 传输数据
     * @param string $url 有效url
     * @param string $json json数据
     * @return string 结果
     */
    public function postJsonCurl($url, $json) {
        $ch = curl_init();
        curl_setopt($ch, CURLOPT_URL, $url);
        curl_setopt($ch, CURLOPT_POST, 1);
        curl_setopt($ch, CURLOPT_POSTFIELDS, $json);
        curl_setopt($ch, CURLOPT_TIMEOUT_MS, 5000);
        curl_setopt($ch,  CURLOPT_RETURNTRANSFER, true);
        $result = curl_exec($ch);
        curl_close ($ch);
        return $result;
    }
    /**
     * 向远程请求的综合方法
     * @param string $url 请求的URL
     * @param mix $data 需要传输的数据。 无需json压缩
     * @return array 已解压的数据
     */
    public function send($url, $data){
        $base = new self();//先实例apibase
        $base->setOnly();//设置唯一变化值，以防攻击
        $base->setData($data);//设置需要传输的数据，可任意数据
        $base->setSign();//生成签名SIGN，必须在setData后执行
        $json = $base->toJson();//生成json
        $json_result = $base->postJsonCurl($url, $json);//curl传输
        return json_decode($json_result, true);//获取结果
    }
    /**
     * 接受请求的数据   
     * @return boolean|array false则是签名错误,非false则是请求的数据
     */
    public function receive() {
        $base = new self();//实例apibase
        $json_str = $base->getRequest();//获取curl请求的数据，即setData设置的数据
        $base->ToArray($json_str);//将json转换成array
        $makeSign = $base->makeSign();//将数组重新计算一次sign
        $reqSign = $base->getSign();//取得传输过的sign
        if($makeSign == $reqSign) {
            $receive_data = $base->getData();//此数据则是setData设置的数据
            return $receive_data;
        } else{
            return false;
        }
    }
}